﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using SharedLib.Helpers;
using Tracy.DataModel;
using System.Text.RegularExpressions;
using Tracy.Messages;
using SharedLib.MessageFramework;
using HtmlAgilityPack;
using SharedLib;
namespace Tracy.Plugins.SourceParsers
{
    public class VeryCDSourceParser : ISourceParser
    {
        public event EventHandler<EventArgsGeneric<SourceParserResult>> OnResultFound;
        public event EventHandler<EventArgsGeneric<SourceEntryPair>> OnParseCompleted;

        private MessageCallback _htmlRetrieved;
        private MessageCallback _xmlRetrieved;

        public VeryCDSourceParser()
        {
            _htmlRetrieved = new MessageCallback(this.OnHtmlRetrieved);
            _xmlRetrieved = new MessageCallback(this.OnXmlRetrieved);
        }
        public XmlDocument GetRawResources(XmlDocument doc)
        {
            XmlDocument rtn = new XmlDocument();
            if (doc != null)
            {
                XmlElement resultNode = rtn.CreateElement("result");
                rtn.AppendChild(resultNode);
                Regex regex = new Regex(@"ed2k://\|file\|[^\|]*\|\d*\|\w*(\|h=\w*)?\|/");
                foreach (Match m in regex.Matches(doc.OuterXml))
                {
                    string ed2kUrl = m.Value;
                    ed2kUrl = ed2kUrl.Replace("%26amp%3B", "%26");//change &amp; to & in file name
                    string title = ed2kUrl.Split('|')[2];
                    title = Uri.UnescapeDataString(title);
                    XmlElement resourceNode = rtn.CreateElement("resource");
                    resourceNode.AppendChild(XmlHelper.CreateElementWithTextValue("url", ed2kUrl, rtn));
                    resourceNode.AppendChild(XmlHelper.CreateElementWithTextValue("title", title, rtn));
                    if (resourceNode.ChildNodes.Count == 0)
                        resultNode.AppendChild(resourceNode);
                    else
                        resultNode.InsertBefore(resourceNode, resultNode.FirstChild);
                }
            }

            return rtn;
        }

        

        #region URL-FeedSource-Entry dict
        Dictionary<MessageData<string>, SourceEntryPair> _urlDict = new Dictionary<MessageData<string>, SourceEntryPair>();
        private object _lockHelperDict = new object();
        private void AddDictItem(MessageData<string> url, FeedSource source, Entry entry)
        {
            lock (_lockHelperDict)
            {
                if (!_urlDict.ContainsKey(url))
                {
                    _urlDict.Add(url, new SourceEntryPair() { Entry = entry, Source = source });
                }
            }
        }

        private void RemoveDictItem(MessageData<string> url)
        {
            lock (_lockHelperDict)
            {
                _urlDict.Remove(url);
            }
        }

        private SourceEntryPair GetDictItem(MessageData<string> url)
        {
            lock (_lockHelperDict)
            {
                return _urlDict[url];
            }
        }
        #endregion

        public void Parse(FeedSource source, Entry entry)
        {
            string url = source.GenerateUrl(entry);
            MessageData<string> data = new MessageData<string>(url);
            AddDictItem(data, source, entry);
            TracyFacade.Instance.MessageDispatcher.EnqueueMessage(new GetDataFromURLMessage(this, data, _htmlRetrieved, "GetDataFromURLHandler_VeryCD"));
        }

        private void OnHtmlRetrieved(IAppMessage msg)
        {
            lock (this)
            {
                if (msg != null && msg is GetDataFromURLMessage && !msg.CancelFlag)
                {
                    if (msg.Exception == null)
                    {
                        MessageData<string> urlData = msg.Data as MessageData<string>;
                        SourceEntryPair pair = GetDictItem(urlData);
                        FeedSource source = pair.Source;
                        Entry entry = pair.Entry;
                        RemoveDictItem(urlData);

                        byte[] data = msg.Result as byte[];
                        string html = Encoding.UTF8.GetString(data);
                        HtmlDocument doc = new HtmlDocument();
                        doc.LoadHtml(html);
                        HtmlNode rootNode = doc.DocumentNode.SelectSingleNode("//div[@class='folders_list']");
                        if (rootNode != null)
                        {
                            HtmlNodeCollection selectedNodes = rootNode.SelectNodes("ul[@id='resultsContainer']/li/div/strong/a");
                            if (selectedNodes != null)
                            {
                                //Item lists
                                foreach (HtmlNode node in selectedNodes) //link title
                                {
                                    string title;
                                    if (node.Attributes.Contains("title"))
                                    {
                                        title = node.Attributes["title"].Value;
                                    }
                                    else
                                    {
                                        title = node.InnerText;
                                    }
                                    if (ResourceFilter.IsTitleMatched(entry, title)) //filter out non-related titles
                                    {
                                        string link = node.Attributes["href"].Value;
                                        if (!link.StartsWith("/")) link = "/" + link;
                                        if (!link.EndsWith("/")) link = link + "/";
                                        string feedLink = "http://www.verycd.com" + link + "feed";
                                        MessageData<string> feedLinkData = new MessageData<string>(feedLink);
                                        AddDictItem(feedLinkData, source, entry);
                                        if (!msg.CancelFlag)
                                        {
                                            Console.WriteLine("Xml to retrieve: " + feedLink);
                                            TracyFacade.Instance.MessageDispatcher.EnqueueMessage(new GetXmlFromURLMessage(this, feedLinkData, _xmlRetrieved, "GetXmlFromURLHandler_VeryCD")); //parse feeds
                                        }
                                    }
                                }
                            }

                            //Next page
                            HtmlNode nextPageNode = rootNode.SelectSingleNode("ul[@class='page']/li/a[@class='next' and @rel='next']");
                            if (nextPageNode != null)
                            {
                                string link = nextPageNode.Attributes["href"].Value;
                                //Get page number
                                Regex pageNum = new Regex("page=\\d+");
                                Match m = pageNum.Match(link);
                                if (m.Success)
                                {
                                    string strNum = m.Value.Substring(m.Value.IndexOf("=") + 1);
                                    int num = Convert.ToInt32(strNum);
                                    if (num <= 3) //Only search in first 3 pages
                                    {
                                        if (!link.StartsWith("/")) link = "/" + link;
                                        string fullLink = "http://www.verycd.com" + link;
                                        MessageData<string> fullLinkData = new MessageData<string>(fullLink);
                                        AddDictItem(fullLinkData, source, entry);
                                        if (!msg.CancelFlag)
                                        {
                                            TracyFacade.Instance.MessageDispatcher.EnqueueMessage(new GetDataFromURLMessage(this, fullLinkData, _htmlRetrieved));
                                        }
                                    }
                                }


                            }
                        }

                        CheckCompletion(source, entry);
                    }
                    else
                    {
                        //TODO: Exception
                    }


                }
            }
        }

        private void OnXmlRetrieved(IAppMessage msg)
        {
            lock (this)
            {
                if (msg != null && msg is GetXmlFromURLMessage && !msg.CancelFlag)
                {
                    MessageData<string> url = msg.Data as MessageData<string>;
                    Console.WriteLine("Xml retrieved: " + url.Data);
                    SourceEntryPair pair = GetDictItem(url);
                    FeedSource source = pair.Source;
                    Entry entry = pair.Entry;
                    RemoveDictItem(url);

                    XmlDocument doc = msg.Result as XmlDocument;
                    XmlDocument resultDoc = GetRawResources(doc);

                    //Notify Caller
                    if (resultDoc.SelectNodes("result/resource").Count > 0)
                    {
                        if (OnResultFound != null) OnResultFound.InvokeEvent(this, new EventArgsGeneric<SourceParserResult>(new SourceParserResult() { ResultDoc = resultDoc, Source = source, Entry = entry }));
                        
                    }

                    CheckCompletion(source, entry);
                }
                else
                {
                    //TODO: Exception
                }
            }
        }

        private void CheckCompletion(FeedSource source, Entry entry)
        {
            //Check whether the parse for current source / entry is done
            if (OnParseCompleted != null)
            {
                bool parseDone = true;
                lock (_lockHelperDict)
                {
                    foreach (SourceEntryPair p in _urlDict.Values)
                    {
                        if (p.Entry == entry && p.Source == source)
                        {
                            parseDone = false;
                            break;
                        }
                    }
                }
                if (parseDone)
                {
                    OnParseCompleted.InvokeEvent(this, new EventArgsGeneric<SourceEntryPair>(new SourceEntryPair() { Source = source, Entry = entry }));
                }
            }
        }


        
    }
}
